package tools

import (
	"context"
	"fmt"
	"strconv"
	"time"

	clientv3 "go.etcd.io/etcd/client/v3"
)

/*
 +----------------------------------------------------------------------
 + Title        : EtcdServiceRegister
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2022-01-06
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : etcd 服务注册
 +				: 文档地址:
 +				: 插件: go.etcd.io/etcd/client/v3
 +----------------------------------------------------------------------
*/

// 定义ETCD服务注册结构体
type EtcdServiceRegister struct {
	client     *clientv3.Client
	watchedKey string
}

/**
 * 创建ETCD服务注册
 *
 * @param []string endpoints ------------------------------------------必传
 * @param string username ---------------------------------------------必传
 * @param string password ---------------------------------------------必传
 * @param string watchedKey -------------------------------------------必传
 * @return *EtcdServiceRegister, error
 * @author huwl
 */
func NewEtcdServiceRegister(endpoints []string, username, password, watchedKey string) (*EtcdServiceRegister, error) {
	// 创建 etcd 连接
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		Username:    username,
		Password:    password,
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		return nil, err
	}

	return &EtcdServiceRegister{
		client:     client,
		watchedKey: watchedKey,
	}, nil
}

/**
 * 保持服务器与etcd的长连接
 *
 * @param int64 ttl --------------------------------------------------必传
 * @return error
 * @author huwl
 */
func (sr *EtcdServiceRegister) KeepAlive(ttl int) {
	// 与etcd建立长连接，并保证连接不断(心跳检测)
	ticker := time.NewTicker(time.Second * time.Duration(ttl))

	go func(ttl int64) {
		for {
			resp, err := sr.client.Get(context.Background(), sr.watchedKey)
			if err != nil {
				fmt.Printf("Failed to get server address: %s", err)
			} else if resp.Count == 0 { //尚未注册
				// 创建租约
				leaseResp, err := sr.client.Grant(context.Background(), ttl)
				if err != nil {
					fmt.Printf("Failed to grant lease: %s", err)
				}

				// 将服务地址注册到 etcd 中
				_, err = sr.client.Put(context.Background(), sr.watchedKey, ServiceHost+":"+strconv.Itoa(ServicePort), clientv3.WithLease(leaseResp.ID))
				if err != nil {
					fmt.Printf("Failed to service register: %s", err)
				}

				// 建立长连接
				ch, err := sr.client.KeepAlive(context.Background(), leaseResp.ID)
				if err != nil {
					fmt.Printf("Failed to keep alive: %s", err)
				}

				// 清空 keepAlive 返回的 channel
				go func() {
					for {
						<-ch
					}
				}()
			}
			<-ticker.C
		}
	}(int64(ttl))
}

/**
 * 取消注册
 *
 * @return *clientv3.DeleteResponse, error
 * @author huwl
 */
func (sr *EtcdServiceRegister) UnRegister() (*clientv3.DeleteResponse, error) {
	return sr.client.Delete(context.Background(), sr.watchedKey)
}
